package cn.com.idmy.auth.router;

import cn.com.idmy.auth.context.Contexts;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.dromara.hutool.core.text.AntPathMatcher;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RouterMatcher {
    @Setter
    @Accessors(fluent = true)
    private static AntPathMatcher antPathMatcher = new AntPathMatcher();

    public static boolean isMatch(@NotNull String pattern, @NotNull String path) {
        return antPathMatcher.match(pattern, path);
    }

    public static boolean isMatch(@NotNull List<String> patterns, @NotNull String path) {
        return patterns.stream().anyMatch(pattern -> isMatch(pattern, path));
    }

    public static boolean isMatch(@NotNull String[] patterns, @NotNull String path) {
        return Arrays.stream(patterns).anyMatch(pattern -> isMatch(pattern, path));
    }

    public static boolean isMatchMethod(@NotNull String[] methods, @NotNull String method) {
        return Arrays.stream(methods).anyMatch(m -> m.equalsIgnoreCase(method));
    }

    @NotNull
    public static Matcher match(@NotNull String... patterns) {
        return new Matcher().match(patterns);
    }

    @NotNull
    public static Matcher notMatch(@NotNull String... patterns) {
        return new Matcher().notMatch(patterns);
    }

    @NotNull
    public static Matcher match(@NotNull List<String> patterns) {
        return new Matcher().match(patterns);
    }

    @NotNull
    public static Matcher notMatch(@NotNull List<String> patterns) {
        return new Matcher().notMatch(patterns);
    }

    @NotNull
    public static Matcher matchMethod(@NotNull String... methods) {
        return new Matcher().matchMethod(methods);
    }

    @NotNull
    public static Matcher notMatchMethod(@NotNull String... methods) {
        return new Matcher().notMatchMethod(methods);
    }

    @NotNull
    public static Matcher match(boolean in) {
        return new Matcher().match(in);
    }

    @NotNull
    public static Matcher notMatch(boolean in) {
        return new Matcher().notMatch(in);
    }

    @NotNull
    public static Matcher match(@NotNull Function<Object, Boolean> fn) {
        return new Matcher().match(fn);
    }

    @NotNull
    public static Matcher notMatch(@NotNull Function<Object, Boolean> fn) {
        return new Matcher().notMatch(fn);
    }

    @NotNull
    public static Matcher match(@NotNull String pattern, @NotNull Runnable fn) {
        return new Matcher().match(pattern, fn);
    }

    @NotNull
    public static Matcher match(@NotNull String pattern, @NotNull Consumer<Matcher> fn) {
        return new Matcher().match(pattern, fn);
    }

    @NotNull
    public static Matcher stop() {
        throw new StopMatchException();
    }

    @NotNull
    public static Matcher quit(@NotNull String msg) {
        throw new QuitMatchException(msg);
    }

    @Data
    @Accessors(chain = true)
    public static class Matcher {
        protected boolean isHit = true;

        @NotNull
        public Matcher reset() {
            isHit = true;
            return this;
        }

        @NotNull
        public String uri() {
            return Contexts.request().uri();
        }

        @NotNull
        public String method() {
            return Contexts.request().method();
        }

        @NotNull
        public Matcher match(@NotNull String... patterns) {
            if (isHit) {
                isHit = isMatch(patterns, uri());
            }
            return this;
        }

        @NotNull
        public Matcher notMatch(@NotNull String... patterns) {
            if (isHit) {
                isHit = !isMatch(patterns, uri());
            }
            return this;
        }

        @NotNull
        public Matcher match(@NotNull List<String> patterns) {
            if (isHit) {
                isHit = isMatch(patterns, uri());
            }
            return this;
        }

        @NotNull
        public Matcher notMatch(@NotNull List<String> patterns) {
            if (isHit) {
                isHit = !isMatch(patterns, uri());
            }
            return this;
        }

        @NotNull
        public Matcher matchMethod(@NotNull String... methods) {
            if (isHit) {
                isHit = isMatch(methods, method());
            }
            return this;
        }

        @NotNull
        public Matcher notMatchMethod(@NotNull String... methods) {
            if (isHit) {
                isHit = !isMatch(methods, method());
            }
            return this;
        }

        @NotNull
        public Matcher match(boolean in) {
            if (isHit) {
                isHit = in;
            }
            return this;
        }

        @NotNull
        public Matcher notMatch(boolean in) {
            if (isHit) {
                isHit = !in;
            }
            return this;
        }

        @NotNull
        public Matcher match(@NotNull Function<Object, Boolean> fn) {
            if (isHit) {
                isHit = fn.apply(this);
            }
            return this;
        }

        @NotNull
        public Matcher notMatch(@NotNull Function<Object, Boolean> fn) {
            if (isHit) {
                isHit = !fn.apply(this);
            }
            return this;
        }

        @NotNull
        public Matcher match(@NotNull String pattern, @NotNull Runnable fn) {
            return match(pattern).check(fn);
        }

        @NotNull
        public Matcher match(@NotNull String pattern, @NotNull Consumer<Matcher> fn) {
            return match(pattern).check(fn);
        }

        @NotNull
        public Matcher check(@NotNull Runnable fn) {
            if (isHit) {
                fn.run();
            }
            return this;
        }

        @NotNull
        public Matcher check(@NotNull Consumer<Matcher> fn) {
            if (isHit) {
                fn.accept(this);
            }
            return this;
        }

        @NotNull
        public Matcher free(@NotNull Consumer<Matcher> fn) {
            if (isHit) {
                try {
                    fn.accept(this);
                } catch (StopMatchException ignored) {
                }
            }
            return this;
        }

        @NotNull
        public Matcher stop() {
            if (isHit) {
                throw new StopMatchException();
            }
            return this;
        }

        @NotNull
        public Matcher quit(@NotNull String message) {
            if (isHit) {
                throw new QuitMatchException(message);
            }
            return this;
        }
    }
}
